#include "monitor.h"
#include <conio.h>

#define RAW1_LEFT_CTRL          0x1D
#define RAW1_RIGHT_CTRL         0x1D    /* same as left */
#define RAW1_LEFT_SHIFT         0x2A
#define RAW1_RIGHT_SHIFT        0x36
#define RAW1_LEFT_ALT           0x38
#define RAW1_RIGHT_ALT          0x38    /* same as left */
#define RAW1_CAPS_LOCK          0x3A
#define RAW1_F1                 0x3B
#define RAW1_F2                 0x3C
#define RAW1_F3                 0x3D
#define RAW1_F4                 0x3E
#define RAW1_F5                 0x3F
#define RAW1_F6                 0x40
#define RAW1_F7                 0x41
#define RAW1_F8                 0x42
#define RAW1_F9                 0x43
#define RAW1_F10                0x44
#define RAW1_NUM_LOCK           0x45
#define RAW1_SCROLL_LOCK        0x46
#define RAW1_F11                0x57
#define RAW1_F12                0x58

#define KBD_BUF_SIZE            64


static void reboot(void)
{
        unsigned temp;

        do
        {
                temp = inb(0x64);
                if((temp & 0x01) != 0)
                {
                        (void)inb(0x60);
                        continue;
                }
        } while((temp & 0x02) != 0);
        outb(0x64, 0xFE);
        while(1)
                /* nothing */;
}


static void write_kbd(unsigned adr, unsigned data)
{
        unsigned long timeout;
        unsigned stat;

        for(timeout = 500000L; timeout != 0; timeout--)
        {
                stat = inb(0x64);
                if((stat & 0x02) == 0)
                        break;
        }
        if(timeout != 0)
                outb(adr, data);
}


static unsigned convert_key(unsigned key)
{
        static const unsigned char set1_map[] =
        {
/* 00 */0,      0x1B,   '1',    '2',    '3',    '4',    '5',    '6',
/* 08 */'7',    '8',    '9',    '0',    '-',    '=',    '\b',   '\t',
/* 10 */'q',    'w',    'e',    'r',    't',    'y',    'u',    'i',
/* 1Dh is left Ctrl */
/* 18 */'o',    'p',    '[',    ']',    '\n',   0,      'a',    's',
/* 20 */'d',    'f',    'g',    'h',    'j',    'k',    'l',    ';',
/* 2Ah is left Shift */
/* 28 */'\'',   '`',    0,      '\\',   'z',    'x',    'c',    'v',
/* 36h is right Shift */
/* 30 */'b',    'n',    'm',    ',',    '.',    '/',    0,      0,
/* 38h is left Alt, 3Ah is Caps Lock */
/* 38 */0,      ' ',    0,      KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5,
/* 45h is Num Lock, 46h is Scroll Lock */
/* 40 */KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,0,      0,      KEY_HOME,
/* 48 */KEY_UP, KEY_PGUP,'-',   KEY_LFT,'5',    KEY_RT, '+',    KEY_END,
/* 50 */KEY_DN, KEY_PGDN,KEY_INS,KEY_DEL,0,     0,      0,      KEY_F11,
/* 58 */KEY_F12
        };
        static unsigned short kbd_status, saw_break_code;
/**/
        unsigned short temp;

/* check for break key (i.e. a key is released) */
        if(key >= 0x80)
        {
                saw_break_code = 1;
                key &= 0x7F;
        }
/* the only break codes we're interested in are Shift, Ctrl, Alt */
        if(saw_break_code)
        {
                if(key == RAW1_LEFT_ALT || key == RAW1_RIGHT_ALT)
                        kbd_status &= ~KBD_META_ALT;
                else if(key == RAW1_LEFT_CTRL || key == RAW1_RIGHT_CTRL)
                        kbd_status &= ~KBD_META_CTRL;
                else if(key == RAW1_LEFT_SHIFT || key == RAW1_RIGHT_SHIFT)
                        kbd_status &= ~KBD_META_SHIFT;
                saw_break_code = 0;
                return 0;
        }
/* it's a make key: check the "meta" keys, as above */
        if(key == RAW1_LEFT_ALT || key == RAW1_RIGHT_ALT)
        {
                kbd_status |= KBD_META_ALT;
                return 0;
        }
        if(key == RAW1_LEFT_CTRL || key == RAW1_RIGHT_CTRL)
        {
                kbd_status |= KBD_META_CTRL;
                return 0;
        }
        if(key == RAW1_LEFT_SHIFT || key == RAW1_RIGHT_SHIFT)
        {
                kbd_status |= KBD_META_SHIFT;
                return 0;
        }
/* Scroll Lock, Num Lock, and Caps Lock set the LEDs. These keys
have on-off (toggle or XOR) action, instead of momentary action */
        if(key == RAW1_SCROLL_LOCK)
        {
                kbd_status ^= KBD_META_SCRL;
                goto LEDS;
        }
        if(key == RAW1_NUM_LOCK)
        {
                kbd_status ^= KBD_META_NUM;
                goto LEDS;
        }
        if(key == RAW1_CAPS_LOCK)
        {
                kbd_status ^= KBD_META_CAPS;
LEDS:           write_kbd(0x60, 0xED);  /* "set LEDs" command */
                temp = 0;
                if(kbd_status & KBD_META_SCRL)
                        temp |= 1;
                if(kbd_status & KBD_META_NUM)
                        temp |= 2;
                if(kbd_status & KBD_META_CAPS)
                        temp |= 4;
                write_kbd(0x60, temp);  /* bottom 3 bits set LEDs */
                return 0;
        }
/* ignore invalid scan codes */
        if(key >= sizeof(set1_map) / sizeof(set1_map[0]))
                return 0;
/* convert raw scancode in key to unshifted ASCII in temp */
        temp = set1_map[key];
/* defective keyboard? non-US keyboard? more than 104 keys? */
        if(temp == 0)
                return temp;
/* handle the three-finger salute */
        if((kbd_status & KBD_META_CTRL) && (kbd_status & KBD_META_ALT) &&
                (temp == KEY_DEL))
        {
                monitor_write("\n*** rebooting!");
                reboot();
        }
/* I really don't know what to do yet with Alt, Ctrl, etc. -- punt */
        return temp;
}

